import json
import os.path
import re
import urllib.parse
from datetime import date
from decimal import Decimal
from functools import wraps
from typing import Tuple

from app import urls
from settings import STATIC_PATH
from utils import MySQL, logger, Config

mysql_conf = Config().get_mysql_config()


def route(path):
    """路由装饰器"""

    def decorator(func):
        urls.route_info[path] = func

        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)

        return wrapper

    return decorator


def convert(row_dicts: dict):
    """序列化字典列表为json字符串

    :param row_dicts:
    :return:
    """
    # 直接json序列化会失败, 先处理字典列表
    for row_dict in row_dicts:
        for key, value in row_dict.items():
            if isinstance(value, Decimal):
                row_dict[key] = float(value)
            elif isinstance(value, date):
                row_dict[key] = str(value)
    logger.debug(row_dicts)
    return json.dumps(row_dicts)


@route('/index.json')
def index() -> bytes:
    """动态请求主页所有股票信息的数据

    :return:
    """
    sql = 'select * from info;'
    logger.debug(sql)

    with MySQL(**mysql_conf) as mysql:
        row_dicts = mysql.query(sql)
    logger.debug(row_dicts)

    query_result = convert(row_dicts)
    return query_result.encode()


@route('/center.json')
def center() -> bytes:
    """动态请求个人中心的数据

    :return:
    """
    sql = '''select 
    code, 
    short, 
    chg, 
    turnover, 
    price, 
    highs, 
    note_info
from info
    join focus on info.id = focus.id;'''

    with MySQL(**mysql_conf) as mysql:
        row_dicts = mysql.query(sql)
    logger.debug(row_dicts)

    query_result = convert(row_dicts)
    return query_result.encode()


def parse_http_request(request: str) -> tuple:
    """解析http请求

    :param request:
    :return:
    """
    req_line = request.splitlines()
    method, path, proto = req_line[0].split()

    # 请求路径进行解码
    path = urllib.parse.unquote(path)
    logger.info(f'请求资源路径: {path}')

    # 设置默认首页
    if path == '/':
        path = '/index.html'

    # 用空字符串替换?后的拼接参数
    pattern = re.compile(r'\?.+')
    if pattern.search(path):
        path = pattern.sub('', path, 1)

    return method, path, proto


def judge_static_request(path: str) -> Tuple[bool, str]:
    """判断请求资源类型是动态资源还是静态资源

    :param path:请求的资源路径
    :return:
    """
    prefix, suffix = os.path.splitext(path)
    content_type = urls.content_type_dict.get(suffix)
    is_static = False if suffix == '.json' else True
    return is_static, content_type


def handle_except_request(path) -> Tuple[int, bytes]:
    """处理请求资源不存在的情况

    :param path:
    :return:
    """
    with open(STATIC_PATH + '/error.html', 'r') as f:
        content = f.read().format(path=path)

    status_code, resp_body = 404, content.encode()
    return status_code, resp_body


def handle_static_request(static: str, path: str) -> Tuple[int, bytes]:
    """处理静态资源请求

    :param static:静态资源根目录
    :param path:请求的资源路径
    :return:
    """
    resource_path = static + path

    try:
        with open(resource_path, 'rb') as f:
            resp_body = f.read()
    except Exception as e:
        logger.error(e)
        status_code, resp_body = handle_except_request(path)
    else:
        status_code, resp_body = 200, resp_body

    return status_code, resp_body


def handle_dynamic_request(path: str) -> Tuple[int, bytes]:
    """处理动态资源请求

    :param path:请求的资源路径
    :return:
    """
    function = urls.route_info.get(path)

    if function:
        status_code, resp_body = 200, function()
    else:
        status_code, resp_body = 404, b'404 Not Found'

    return status_code, resp_body


def create_http_response(status_code: int, resp_body: bytes, content_type: str) -> bytes:
    """组装http响应报文并返回

    :param status_code:响应状态码
    :param resp_body:响应主体内容
    :param content_type:响应内容类型
    :return:
    """
    # 响应行
    resp_line = 'HTTP/1.1 200 ok\r\n'
    if status_code == 404:
        resp_line = 'HTTP/1.1 404 Not Found\r\n'

    # 响应头
    resp_head = 'Server: PWB/1.1\r\n'
    resp_head += 'Connection: keep-alive\r\n'
    if content_type:
        resp_head += 'Content-Type: %s\r\n' % content_type

    # 组装http响应报文
    resp_msg = (resp_line + resp_head + '\r\n').encode() + resp_body
    return resp_msg


def application(static: str, request: str) -> bytes:
    """处理http请求并返回响应"
    
    :param static: 静态资源根目录
    :param request: http请求报文
    :return: 
    """""
    method, path, _ = parse_http_request(request)
    is_static, content_type = judge_static_request(path)
    if is_static:  # 处理静态资源请求
        status_code, resp_body = handle_static_request(static, path)
    else:  # 处理动态资源请求
        status_code, resp_body = handle_dynamic_request(path)
    resp_msg = create_http_response(status_code, resp_body, content_type)
    return resp_msg
